package com.hq.ims.data.service;

import com.hq.ims.data.config.verification.VdAdd;
import com.hq.ims.data.config.verification.VdEdit;
import com.hq.ims.data.dao.PermissionMenuMapper;
import com.hq.ims.data.entity.CompanyDepartmentEmployee;
import com.hq.ims.data.entity.CompanyEmployee;
import com.hq.ims.data.entity.PermissionMenu;
import com.hq.ims.data.entity.User;
import com.hq.ims.data.entityenum.EnumMenuOwnType;
import com.hq.ims.data.utils.ErrMsgConstant;
import com.hq.ims.data.vo.PermissionMenuMapstruct;
import com.hq.mybatis.base.BaseService;
import com.hq.utils.exception.CommonException;
import com.hq.utils.util.*;
import com.hq.web.penum.EnumMenuType;
import com.hq.web.utils.SpringBootValidateUtils;
import lombok.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * jiang bai ping
 *
 * @Email 526666031@qq.com
 * @Detail 权限点服务实现
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class BasePermissionMenuService extends BaseService<PermissionMenuMapper, PermissionMenu> {

    @Resource
    private BasePermissionMapAuthorityMenuService basePermissionMapAuthorityMenuService;
    @Resource
    private BasePermissionMapAuthorityUserService basePermissionMapAuthorityUserService;
    @Resource
    private BaseCompanyDepartmentEmployeeService baseCompanyDepartmentEmployeeService;
    @Resource
    private BasePermissionMapAuthorityDepartmentService basePermissionMapAuthorityDepartmentService;
    @Resource
    private BasePermissionMapAuthorityRoleService basePermissionMapAuthorityRoleService;
    @Resource
    private BasePermissionMapRoleUserService basePermissionMapRoleUserService;
    @Resource
    private BaseUserService baseUserService;
    @Resource
    private BaseCompanyService baseCompanyService;
    @Resource
    private BaseCompanyEmployeeService baseCompanyEmployeeService;

    /**
     * 添加权限点
     */
    @Transactional(rollbackFor = Exception.class)
    public void addMenu(PermissionMenuMapstruct.SaveVo saveVo) {
        SpringBootValidateUtils.validate(saveVo, VdAdd.class);
        //校验权限点名称是否重复
        boolean existsMenuName = this.isExistsMenuName(saveVo.getParentId(), saveVo.getMenuName(), saveVo.getOwnType(), null);
        BooleanUtils.isTrueThrow(existsMenuName, ErrMsgConstant.PERMISSION_MENU_NAME_EXISTS);

        //创建权限点保存对象
        PermissionMenu menu = new PermissionMenu();
        menu.setMenuName(saveVo.getMenuName());
        menu.setOrderNumber(saveVo.getOrderNumber());
        menu.setType(saveVo.getType());
        menu.setPageKey(saveVo.getPageKey());
        menu.setAppPageKey(saveVo.getAppPageKey());
        menu.setPageParam(saveVo.getPageParam());
        menu.setIcon(saveVo.getIcon());
        menu.setIsShow(saveVo.getIsShow());
        menu.setRemark(saveVo.getRemark());
        menu.setRoutePath(saveVo.getRoutePath());
        menu.setRouteName(saveVo.getRouteName());
        menu.setComponentPath(saveVo.getComponentPath());


        PermissionMenu parentMenu = this.getById(saveVo.getParentId());
        //若父id不存在则此为根节点
        if (parentMenu == null) {
            SpringBootValidateUtils.validate(saveVo, PermissionMenuMapstruct.VdAddRootNode.class);
            menu.setPermissionMenuId(SeqUtils.generateUuidNoHorizontalLine());
            menu.setParentId(StringUtils.FH_ZERO);
            menu.setRootId(menu.getPermissionMenuId());
            menu.setLevel(1);
            menu.setOwnType(saveVo.getOwnType());
        } else {
            menu.setParentId(parentMenu.getPermissionMenuId());
            menu.setRootId(parentMenu.getRootId());
            menu.setLevel(parentMenu.getLevel() + 1);
            menu.setOwnType(parentMenu.getOwnType());
        }
        menu.insert();
    }

    /**
     * 根据parentId递归监测该层级是否有重名权限点
     *
     * @param parentId 父ID
     * @param menuName 菜单名称
     */
    private boolean isExistsMenuName(
            @NonNull String parentId,
            @NonNull String menuName,
            @NonNull EnumMenuOwnType ownType,
            String excludeId
    ) {
        Long count = this.lambdaQuery()
                .eq(PermissionMenu::getParentId, parentId)
                .eq(PermissionMenu::getMenuName, menuName)
                .eq(PermissionMenu::getOwnType, ownType)
                .ne(StringUtils.isNotEmpty(excludeId), PermissionMenu::getPermissionMenuId, excludeId)
                .count();
        return count > 0;
    }

    /**
     * 更新权限点
     */
    public void updateMenu(PermissionMenuMapstruct.SaveVo saveVo) {
        //数据校验
        SpringBootValidateUtils.validate(saveVo, VdEdit.class);
        boolean existsMenuName = this.isExistsMenuName(saveVo.getParentId(), saveVo.getMenuName(), saveVo.getOwnType(), saveVo.getPermissionMenuId());
        BooleanUtils.isTrueThrow(existsMenuName, ErrMsgConstant.PERMISSION_MENU_NAME_EXISTS);

        //获取权限点
        PermissionMenu menu = this.getById(saveVo.getPermissionMenuId());
        ObjectUtils.isNullThrow(menu, ErrMsgConstant.PERMISSION_MENU_NOT_EXISTS);
        menu.setMenuName(saveVo.getMenuName());
        menu.setOrderNumber(saveVo.getOrderNumber());
        menu.setType(saveVo.getType());
        menu.setPageKey(saveVo.getPageKey());
        menu.setAppPageKey(saveVo.getAppPageKey());
        menu.setPageParam(saveVo.getPageParam());
        menu.setIcon(saveVo.getIcon());
        menu.setIsShow(saveVo.getIsShow());
        menu.setRemark(saveVo.getRemark());
        menu.setRoutePath(saveVo.getRoutePath());
        menu.setRouteName(saveVo.getRouteName());
        menu.setComponentPath(saveVo.getComponentPath());

        PermissionMenu parentMenu = this.getById(saveVo.getParentId());
        //若父id不存在则此为根节点
        if (parentMenu == null) {
            menu.setParentId(StringUtils.FH_ZERO);
            menu.setRootId(menu.getPermissionMenuId());
            menu.setLevel(1);
            menu.setOwnType(saveVo.getOwnType());
        } else {
            menu.setParentId(parentMenu.getPermissionMenuId());
            menu.setRootId(parentMenu.getRootId());
            menu.setLevel(parentMenu.getLevel() + 1);
            menu.setOwnType(parentMenu.getOwnType());
        }
        boolean idAndParentIdIsThinkAlike = menu.getPermissionMenuId().equals(menu.getParentId());
        BooleanUtils.isTrueThrow(idAndParentIdIsThinkAlike, "当前父ID不能是自已");
        menu.updateById();
    }

    /**
     * 移动菜单
     *
     * @param curMenuId    Cur菜单id
     * @param targetMenuId 目标菜单id
     */
    @Transactional(rollbackFor = Exception.class)
    public void moveMenu(String curMenuId, String targetMenuId) {
        PermissionMenu curMenu = this.getById(curMenuId);
        ObjectUtils.isNullThrow(curMenu, "当前菜单信息为空");
        PermissionMenu targetMenu = this.getById(targetMenuId);
        ObjectUtils.isNullThrow(targetMenu, "目标菜单信息为空");

        //创建权限点接收对象
        List<PermissionMenu> updateMenuList = new ArrayList<>();
        //获取当前菜单及以下菜单集合
        this.getCurrentAndChildMenu(curMenuId, updateMenuList);

        //判断菜单是否移动但自己及以下菜单
        if (updateMenuList.stream().anyMatch(d -> d.getPermissionMenuId().equals(targetMenuId))) {
            throw CommonException.build("无法移动到自己及以下的菜单");
        }

        for (PermissionMenu menu : updateMenuList) {
            if (menu.getPermissionMenuId().equals(curMenuId)) {
                menu.setParentId(targetMenu.getPermissionMenuId());
                menu.setOwnType(targetMenu.getOwnType());
            }
            menu.setRootId(targetMenu.getRootId());
        }
        this.updateBatchById(updateMenuList);
    }

    /**
     * 递归删除
     *
     * @param menuIdList 菜单ID
     */
    public void delByMenuIdList(List<String> menuIdList) {
        if (ListUtils.isNotExistsEle(menuIdList)) {
            return;
        }
        List<PermissionMenu> childrenPermissionMenuList = this.lambdaQuery().in(PermissionMenu::getParentId, menuIdList).list();
        //获取所有的id集合
        if (ListUtils.isExistsEle(childrenPermissionMenuList)) {
            for (String menuId : menuIdList) {
                //删除菜单和权限的绑定关系
                basePermissionMapAuthorityMenuService.delByMenuId(menuId);
            }
            List<String> childrenMenuIdList = childrenPermissionMenuList.stream().map(PermissionMenu::getPermissionMenuId).collect(Collectors.toList());
            this.delByMenuIdList(childrenMenuIdList);
        }
        this.lambdaUpdate().in(PermissionMenu::getPermissionMenuId, menuIdList).remove();

    }


    /**
     * 组装菜单树
     */
    public List<PermissionMenuMapstruct.PermissionMenuTreeVo> assembleMenuTree(List<? extends PermissionMenu> permissionMenuList) {
        //根据权限点集合获取菜单路由树
        List<PermissionMenuMapstruct.PermissionMenuTreeVo> sysMenuVoList = JsonUtils.convertListObject(permissionMenuList, PermissionMenuMapstruct.PermissionMenuTreeVo.class);
        //过滤出的菜单
        List<PermissionMenuMapstruct.PermissionMenuTreeVo> menuList = new ArrayList<>();
        //过滤出的按钮
        List<PermissionMenuMapstruct.PermissionMenuTreeVo> buttonList = new ArrayList<>();
        //菜单映射
        Map<String, PermissionMenuMapstruct.PermissionMenuTreeVo> menuMap = new HashMap<>(sysMenuVoList.size());

        //将菜单和按钮分开处理
        for (PermissionMenuMapstruct.PermissionMenuTreeVo m : sysMenuVoList) {
            if (m.getType() == EnumMenuType.Menu) {
                menuList.add(m);
                menuMap.put(m.getPermissionMenuId(), m);
            } else {
                buttonList.add(m);
            }
        }

        //将button添加到对应的菜单中
        for (PermissionMenuMapstruct.PermissionMenuTreeVo m : buttonList) {
            if (menuMap.containsKey(m.getParentId())) {
                menuMap.get(m.getParentId()).getChildrenButtonList().add(m);
            }
        }

        String defaultParentVal = StringUtils.FH_ZERO;
        sysMenuVoList = ParentChildrenClassificationUtils.classificationToTree(
                menuList,
                d -> d.getPermissionMenuId(),
                d -> d.getParentId(),
                (d, c) -> d.setChildrenMenuList(c),
                d -> d.setParentId(defaultParentVal),
                defaultParentVal
        );
        return sysMenuVoList;
    }

    /**
     * 获得当前和子菜单
     *
     * @param parentMenuId 父菜单id
     * @param saveList     所有菜单
     */
    public void getCurrentAndChildMenu(
            @NonNull String parentMenuId,
            @NonNull List<PermissionMenu> saveList
    ) {
        //获取当前节点菜单
        PermissionMenu parentMenu = this.getById(parentMenuId);
        if (ObjectUtils.isNull(parentMenu)) {
            return;
        }
        saveList.add(parentMenu);

        //获取当前节点子菜单
        this.getAllChildMenu(parentMenuId, saveList);
    }


    /**
     * 得到所有子菜单
     *
     * @param parentMenuId       父菜单id
     * @param permissionMenuList 所有菜单
     */
    private void getAllChildMenu(
            @NonNull String parentMenuId,
            @NonNull List<PermissionMenu> permissionMenuList
    ) {
        //获取当前节点的子节点
        List<PermissionMenu> childMenus = this.lambdaQuery()
                .eq(PermissionMenu::getParentId, parentMenuId)
                .orderByAsc(PermissionMenu::getOrderNumber)
                .list();
        permissionMenuList.addAll(childMenus);
        if (ListUtils.isExistsEle(childMenus)) {
            for (PermissionMenu childMenu : childMenus) {
                this.getAllChildMenu(childMenu.getPermissionMenuId(), permissionMenuList);
            }
        }
    }

    /**
     * 获取菜单列表
     */
    public List<PermissionMenuMapstruct.PermissionMenuTreeVo> getTree() {
        List<PermissionMenu> list = this.list();
        List<PermissionMenuMapstruct.PermissionMenuTreeVo> permissionMenuTreeVoList = this.assembleMenuTree(list);
        return permissionMenuTreeVoList;
    }

    /**
     * 获取菜单列表
     */
    public List<PermissionMenuMapstruct.PermissionMenuTreeVo> getTree(EnumMenuOwnType menuOwnType) {
        List<PermissionMenu> list = this.lambdaQuery().eq(PermissionMenu::getOwnType, menuOwnType).orderByAsc(PermissionMenu::getOrderNumber).list();
        List<PermissionMenuMapstruct.PermissionMenuTreeVo> permissionMenuTreeVoList = this.assembleMenuTree(list);
        return permissionMenuTreeVoList;
    }


    /**
     * 被角色id菜单
     *
     * @param roleIdList 角色id
     */
    public List<PermissionMenuMapstruct.PermissionMenuVo> getByRoleIdList(@NonNull List<String> roleIdList) {
        List<String> menuIdList = basePermissionMapAuthorityMenuService.getMenuIdListByAuthIdList(roleIdList);
        if (ListUtils.isNotExistsEle(menuIdList)) {
            return ListUtils.newEmptyList();
        }
        List<PermissionMenu> list = this.lambdaQuery().in(PermissionMenu::getPermissionMenuId, menuIdList).list();
        List<PermissionMenuMapstruct.PermissionMenuVo> voList = PermissionMenuMapstruct.INSTANCE.toPermissionMenuVoList(list);
        return voList;
    }

    /**
     * 根据组ID获取菜单
     *
     * @param authIdList 组Id
     */
    public List<PermissionMenuMapstruct.PermissionMenuVo> getByAuthIdList(@NonNull List<String> authIdList) {
        List<String> menuIdList = basePermissionMapAuthorityMenuService.getMenuIdListByAuthIdList(authIdList);
        if (ListUtils.isNotExistsEle(menuIdList)) {
            return ListUtils.newEmptyList();
        }
        List<PermissionMenu> permissionMenuList = this.lambdaQuery()
                .in(PermissionMenu::getPermissionMenuId, menuIdList)
                .orderByAsc(PermissionMenu::getLevel)
                .orderByAsc(PermissionMenu::getOrderNumber)
                .list();

        List<PermissionMenuMapstruct.PermissionMenuVo> voList = PermissionMenuMapstruct.INSTANCE.toPermissionMenuVoList(permissionMenuList);
        return voList;
    }

    /**
     * 获取用户菜单
     *
     * @param deptIdList 部门id
     */
    public List<PermissionMenuMapstruct.PermissionMenuVo> getByDepartIdList(@NonNull List<String> deptIdList) {
        List<String> authIdList = basePermissionMapAuthorityDepartmentService.getAuthIdListByDeptIdList(deptIdList);
        if (ListUtils.isNotExistsEle(authIdList)) {
            return ListUtils.newEmptyList();
        }
        return this.getByAuthIdList(authIdList);
    }


    //------------------------------ 获取用户权限 ----------------------------------

    /**
     * 得到用户所有菜单用户id
     *
     * @param userId 用户id
     */
    public List<PermissionMenuMapstruct.PermissionMenuTreeVo> getTreeByUserIdAndCompanyId(
            @NonNull String userId,
            @NonNull String companyId,
            EnumMenuOwnType menuOwnType
    ) {
        List<PermissionMenuMapstruct.PermissionMenuVo> voList = this.getMenuByUserIdAndCompanyId(userId, companyId, menuOwnType);
        if (ListUtils.isNotExistsEle(voList)) {
            return ListUtils.newEmptyList();
        }
        //组装菜单树
        return this.assembleMenuTree(voList);
    }

    /**
     * 获取用户所有菜单
     *
     * @param userId 用户id
     */
    public List<PermissionMenuMapstruct.PermissionMenuVo> getMenuByUserIdAndCompanyId(
            @NonNull String userId,
            @NonNull String companyId,
            EnumMenuOwnType menuOwnType
    ) {
        User user = baseUserService.getById(userId);
        //就用不存在就给空的权限菜单
        if (user == null) {
            return ListUtils.newEmptyList();
        }
        //收集用户全部菜单
        List<PermissionMenu> permissionMenuList = new ArrayList<>();
        //如果是平台管理员就给全部菜单,不是管理员就要获取有权限的菜单
        if (user.getIsAdmin()) {
            permissionMenuList = this.lambdaQuery().eq(menuOwnType != null, PermissionMenu::getOwnType, menuOwnType).list();
        } else {
            CompanyEmployee companyEmployeeAdmin = baseCompanyEmployeeService.getCompanyAdminByCompanyId(companyId);
            if (companyEmployeeAdmin.getUserId().equals(user.getUserId())) {
                //是公司管理员
                permissionMenuList = this.lambdaQuery().eq(menuOwnType != null, PermissionMenu::getOwnType, menuOwnType).list();
            } else {
                //其它用户
                //1 获取用户绑定的权限
                Set<String> authIdSet = new HashSet<>();
                List<String> authIdList = basePermissionMapAuthorityUserService.getAuthIdListByUserIdAndCompanyId(userId, companyId);
                for (String authId : authIdList) {
                    authIdSet.add(authId);
                }
                //2 获取用户绑定的部门权限
                List<CompanyDepartmentEmployee> companyDepartmentEmployeeList = baseCompanyDepartmentEmployeeService.getByUserIdAndCompanyId(userId, companyId, null);
                if (ListUtils.isExistsEle(companyDepartmentEmployeeList)) {
                    List<String> departmentIdList = companyDepartmentEmployeeList.stream().map(CompanyDepartmentEmployee::getCompanyDepartmentId).distinct().collect(Collectors.toList());
                    authIdList = basePermissionMapAuthorityDepartmentService.getAuthIdListByDeptIdList(departmentIdList);
                    for (String authId : authIdList) {
                        authIdSet.add(authId);
                    }
                }
                //3 获取用户的角色
                List<String> roleIdList = basePermissionMapRoleUserService.getRoleIdByUserIdAndCompanyId(userId, companyId);
                authIdList = basePermissionMapAuthorityRoleService.getAuthIdListByRoleIdList(roleIdList);
                for (String authId : authIdList) {
                    authIdSet.add(authId);
                }

                List<String> menuIdList = basePermissionMapAuthorityMenuService.getMenuIdListByAuthIdList(authIdList);
                if (ListUtils.isNotExistsEle(menuIdList)) {
                    return ListUtils.newEmptyList();
                }
                permissionMenuList = this.lambdaQuery().in(PermissionMenu::getPermissionMenuId, menuIdList)
                        .eq(menuOwnType != null, PermissionMenu::getOwnType, menuOwnType)
                        .list();
            }
        }

        if (ListUtils.isNotExistsEle(permissionMenuList)) {
            return ListUtils.newEmptyList();
        }
        //菜单去重
        permissionMenuList = permissionMenuList.stream().distinct().sorted(Comparator.comparing(PermissionMenu::getOrderNumber)).collect(Collectors.toList());
        List<PermissionMenuMapstruct.PermissionMenuVo> voList = PermissionMenuMapstruct.INSTANCE.toPermissionMenuVoList(permissionMenuList);
        return voList;
    }


}
